#include <stdio.h>
#include <math.h>
#include <assert.h>
#include "aimage.h"
#include "accumulate.h"

ACCUMULATOR::ACCUMULATOR()
{
	m_height = -1;
	m_width = -1;
	m_image = NULL;
}

ACCUMULATOR::~ACCUMULATOR( void )
{
	free_image();
}

void ACCUMULATOR::alloc_image( int width, int height )
{
	int y;

	if ( m_width == width && m_height == height )
	{
		return;
	}
	free_image();

	m_width = width;
	m_height = height;

	assert( m_width>0 && m_width < 10*1024 );
	assert( m_height>0 && m_height < 10*1024 );

	m_image = new ACCUM_PIXEL *[ m_height ];
	m_image[0] = new ACCUM_PIXEL [ m_height * m_width ];

	for ( y = 1; y< m_height; ++y )
		m_image[ y ] = m_image[ 0 ]+ m_width * y;

	for ( y = 0; y < m_height; ++y )
	{
		int x;
		for ( x = 0; x < m_width; ++x )
		{
			m_image[ y ][ x ].r = 0;
			m_image[ y ][ x ].g = 0;
			m_image[ y ][ x ].b = 0;
			m_image[ y ][ x ].samples = 0;
		}
	}
}


void ACCUMULATOR::cls( void )
{
	int y;
	for ( y = 0; y < m_height; ++y )
	{
		int x;
		for ( x = 0; x < m_width; ++x )
		{
			m_image[ y ][ x ].r = 0;
			m_image[ y ][ x ].g = 0;
			m_image[ y ][ x ].b = 0;
			m_image[ y ][ x ].samples = 0;
		}
	}
}

void ACCUMULATOR::free_image( void )
{
	if ( m_image != NULL )
	{
		delete[] m_image[0];
		delete[] m_image;
		m_image = NULL;
		m_width = -1;
		m_height = -1;
	}
	assert( m_width == -1 );
	assert( m_height == -1 );
}

ACCUMULATOR::const_iterator ACCUMULATOR::const_begin( void ) const
{
	ACCUMULATOR::const_iterator b;
	b.p = m_image[ 0 ];
        return b;
}

ACCUMULATOR::const_iterator ACCUMULATOR::const_end( void ) const
{
        ACCUMULATOR::const_iterator e;
        e.p = m_image[ 0 ] + ( m_width * m_height );
        return e;
}

void ACCUMULATOR::to_image( AIMAGE &dst )
{
	int iwidth, iheight;

	dst.get_dim( iwidth, iheight );

	assert( iwidth == m_width );
	assert( iheight == m_height );

	AIMAGE::iterator cur_dst, end_dst;
	ACCUMULATOR::const_iterator cur_src, end_src;

	cur_dst = dst.begin();
	end_dst = dst.end();
	cur_src = const_begin();
	end_src = const_end();

	while ( cur_dst != end_dst )
	{
		const ACCUM_PIXEL &src = *cur_src;
		APIXEL &dst = *cur_dst;
		
		if ( src.samples >= 1 )
		{
			int r,g,b;

			r = src.r;
			g = src.g;
			b = src.b;

			if ( r < 0 )
			{
				r = 0;
			}
			if ( g < 0 )
			{
				g = 0;
			}
			if ( b < 0 )
			{
				b = 0;
			}
			dst.r = r / src.samples;
			dst.g = g / src.samples;
			dst.b = b / src.samples;
		}
		else
		{
			dst.r = 0;
			dst.g = 0;
			dst.b = 0;
		}
		++cur_dst;
		++cur_src;
	}
}

void ACCUMULATOR::rotate_and_add( 
	const AIMAGE &frame,
	ASTAR_TRANSFORM &t 
)
{
	int y;

	int fwidth, fheight;

	frame.get_dim( fwidth, fheight );
	assert( fwidth == m_width );
	assert( fheight == m_height );

	const float xc = fwidth / 2 ; //5184 / 2;
	const float yc = fheight / 2; //3456 / 2;

	APIXEL p;
	APIXEL alphap;

        for ( y = 0; y < m_height; ++y )
        {
                int x;
                for ( x = 0; x < m_width; ++x )
                {
                        float xs, ys;
                        float xd, yd;
                        int xdi, ydi;

                        xs = x - xc;
                        ys = y - yc;

                        xd = cosf( t.angle ) * xs - sinf( t.angle ) * ys + t.xo;
                        yd = sinf( t.angle ) * xs + cosf( t.angle ) * ys + t.yo;

                        xdi = xd;
                        ydi = yd;

                        xdi += xc;
                        ydi += yc;

                        if ( xdi < 0 || xdi >= m_width || ydi < 0 || ydi >= m_height )
                        {
                                continue;
                        }

                        frame.getpixel( xdi, ydi, p );

			ACCUM_PIXEL &dst_p = m_image[ y ][ x ];
			dst_p.r += p.r;		
			dst_p.g += p.g;		
			dst_p.b += p.b;		
			dst_p.samples += 1;
                }
        }
}

void ACCUMULATOR::rotate_and_sub_dark( 
	const AIMAGE &frame,
	ASTAR_TRANSFORM &t 
)
{
	int y;

	int fwidth, fheight;

	frame.get_dim( fwidth, fheight );
	assert( fwidth == m_width );
	assert( fheight == m_height );

	const float xc = fwidth / 2 ; //5184 / 2;
	const float yc = fheight / 2; //3456 / 2;

	APIXEL p;
	APIXEL alphap;

        for ( y = 0; y < m_height; ++y )
        {
                int x;
                for ( x = 0; x < m_width; ++x )
                {
                        float xs, ys;
                        float xd, yd;
                        int xdi, ydi;

                        xs = x - xc;
                        ys = y - yc;

                        xd = cosf( t.angle ) * xs - sinf( t.angle ) * ys + t.xo;
                        yd = sinf( t.angle ) * xs + cosf( t.angle ) * ys + t.yo;

                        xdi = xd;
                        ydi = yd;

                        xdi += xc;
                        ydi += yc;

                        if ( xdi < 0 || xdi >= m_width || ydi < 0 || ydi >= m_height )
                        {
                                continue;
                        }

                        frame.getpixel( xdi, ydi, p );

			ACCUM_PIXEL &dst_p = m_image[ y ][ x ];
			dst_p.r -= p.r;		
			dst_p.g -= p.g;		
			dst_p.b -= p.b;		
                }
        }
}

void ACCUMULATOR::normalize( void ) 
{
	int y;
	for ( y = 0; y < m_height; ++y )
	{
		int x;
		for ( x = 0; x < m_width; ++x )
		{
			int samples = m_image[ y ][ x ].samples;
			if ( samples != 0 )
			{
				m_image[ y ][ x ].r /= samples;
				m_image[ y ][ x ].g /= samples;
				m_image[ y ][ x ].b /= samples;
				m_image[ y ][ x ].samples = 1;
			}
		}
	}
}

void ACCUMULATOR::sum_of_square( const ACCUMULATOR &frame, const ACCUMULATOR &normals )
{
	int y;
	for ( y = 0; y < m_height; ++y )
	{
		int x;
		for ( x = 0; x < m_width; ++x )
		{
			int frame_samples = frame.m_image[ y ][ x ].samples;
			int normal_samples = normals.m_image[ y ][ x ].samples;
			assert( frame_samples == 0 || frame_samples == 1 );
			assert( normal_samples == 0 || normal_samples == 1 );
			if ( frame_samples == 1 && normal_samples == 1 )
			{
				int rd = frame.m_image[ y ][ x ].r - normals.m_image[ y ][ x ].r;
				int gd = frame.m_image[ y ][ x ].g - normals.m_image[ y ][ x ].g;
				int bd = frame.m_image[ y ][ x ].b - normals.m_image[ y ][ x ].b;

				int square_r = rd * rd;
				int square_g = gd * gd;
				int square_b = bd * bd;

				if (( square_r >> 4 ) + ( m_image[ y ][ x ].r >> 4) < 
						(( ACCUM_FULL_INT >> 4 ) - 1) )
				{
					m_image[ y ][ x ].r += square_r;
				}
				else
				{
					m_image[ y ][ x ].r += ACCUM_FULL_INT;
				}

				if (( square_g >> 4 ) + ( m_image[ y ][ x ].g >> 4) < 
						(( ACCUM_FULL_INT >> 4 ) - 1) )
				{
					m_image[ y ][ x ].g += square_g;
				}
				else
				{
					m_image[ y ][ x ].g += ACCUM_FULL_INT;
				}

				if (( square_b >> 4 ) + ( m_image[ y ][ x ].b >> 4) < 
						(( ACCUM_FULL_INT >> 4 ) - 1) )
				{
					m_image[ y ][ x ].b += square_b;
				}
				else
				{
					m_image[ y ][ x ].b += ACCUM_FULL_INT;
				}
			}
		}
	}
}

